package net.realsee.service.wcs.intf.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import net.realsee.common.constant.CacheNames;
import net.realsee.common.enums.wcs.intf.InterfaceClassEnum;
import net.realsee.common.enums.wcs.intf.InterfaceMethodEnum;
import net.realsee.common.enums.wcs.intf.InterfaceTypeEnum;
import net.realsee.common.exception.ServiceException;
import net.realsee.common.utils.JsonUtils;
import net.realsee.common.utils.StringUtils;
import net.realsee.common.utils.redis.CacheUtils;
import net.realsee.common.utils.spring.SpringUtils;
import net.realsee.domain.wcs.intf.WcsExtApp;
import net.realsee.domain.wcs.intf.WcsIntf;
import net.realsee.domain.wcs.intf.bo.InterfaceRequest;
import net.realsee.domain.wcs.intf.bo.WcsIntfBo;
import net.realsee.domain.wcs.intf.vo.WcsIntfVo;
import net.realsee.framework.core.domain.PageQuery;
import net.realsee.framework.core.page.TableDataInfo;
import net.realsee.mapper.wcs.intf.WcsIntfMapper;
import net.realsee.service.wcs.intf.IWcsExtAppService;
import net.realsee.service.wcs.intf.IWcsIntfService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 接口管理Service业务层处理
 *
 * @author realsee
 * @date 2023-08-11
 */
@RequiredArgsConstructor
@Service
public class WcsIntfServiceImpl extends ServiceImpl<WcsIntfMapper, WcsIntf> implements IWcsIntfService {

    private final WcsIntfMapper baseMapper;
    private final IWcsExtAppService extAppService;

    /**
     * 查询接口管理
     */
    @Override
    public WcsIntfVo selectVoById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询接口管理
     */
    @Cacheable(cacheNames = CacheNames.ITF_CODE, key = "#interfaceCode")
    @Override
    public WcsIntf getByInterfaceCode(String interfaceCode) {
        return this.lambdaQuery().eq(WcsIntf::getInterfaceCode, interfaceCode).last("limit 1").one();
    }

    /**
     * 查询接口管理列表
     */
    @Override
    public TableDataInfo<WcsIntfVo> selectVoPage(WcsIntfBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<WcsIntf> lqw = buildVoQueryWrapper(bo);
        Page<WcsIntfVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询接口管理列表
     */
    @Override
    public List<WcsIntfVo> selectVoList(WcsIntfBo bo) {
        LambdaQueryWrapper<WcsIntf> lqw = buildVoQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<WcsIntf> buildVoQueryWrapper(WcsIntfBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<WcsIntf> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getId() != null, WcsIntf::getId, bo.getId());
        lqw.eq(StringUtils.isNotBlank(bo.getAppCode()), WcsIntf::getAppCode, bo.getAppCode());
        lqw.eq(StringUtils.isNotBlank(bo.getInterfaceCode()), WcsIntf::getInterfaceCode, bo.getInterfaceCode());
        lqw.eq(StringUtils.isNotBlank(bo.getInterfaceClass()), WcsIntf::getInterfaceClass, bo.getInterfaceClass());
        lqw.like(StringUtils.isNotBlank(bo.getInterfaceName()), WcsIntf::getInterfaceName, bo.getInterfaceName());
        lqw.eq(StringUtils.isNotBlank(bo.getInterfaceDesc()), WcsIntf::getInterfaceDesc, bo.getInterfaceDesc());
        lqw.eq(StringUtils.isNotBlank(bo.getInterfaceType()), WcsIntf::getInterfaceType, bo.getInterfaceType());
        lqw.eq(StringUtils.isNotBlank(bo.getMethod()), WcsIntf::getMethod, bo.getMethod());
        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), WcsIntf::getUrl, bo.getUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getOperationCode()), WcsIntf::getOperationCode, bo.getOperationCode());
        lqw.eq(StringUtils.isNotBlank(bo.getIsRecord()), WcsIntf::getIsRecord, bo.getIsRecord());
        lqw.eq(StringUtils.isNotBlank(bo.getAllowRetry()), WcsIntf::getAllowRetry, bo.getAllowRetry());
        lqw.eq(StringUtils.isNotBlank(bo.getRetryType()), WcsIntf::getRetryType, bo.getRetryType());
        lqw.eq(bo.getMaxRetry() != null, WcsIntf::getMaxRetry, bo.getMaxRetry());
        lqw.eq(StringUtils.isNotBlank(bo.getAuthType()), WcsIntf::getAuthType, bo.getAuthType());
        lqw.eq(StringUtils.isNotBlank(bo.getAuthData()), WcsIntf::getAuthData, bo.getAuthData());
        lqw.eq(bo.getTimeoutTime() != null, WcsIntf::getTimeoutTime, bo.getTimeoutTime());
        lqw.eq(StringUtils.isNotBlank(bo.getAtteParam()), WcsIntf::getAtteParam, bo.getAtteParam());
        lqw.eq(StringUtils.isNotBlank(bo.getFuid()), WcsIntf::getFuid, bo.getFuid());
        lqw.eq(bo.getTextId() != null, WcsIntf::getTextId, bo.getTextId());
        lqw.eq(bo.getRefrenceId() != null, WcsIntf::getRefrenceId, bo.getRefrenceId());
        lqw.eq(bo.getUnitId() != null, WcsIntf::getUnitId, bo.getUnitId());
        lqw.eq(bo.getDelFlag() != null, WcsIntf::getDelFlag, bo.getDelFlag());
        lqw.eq(StringUtils.isNotBlank(bo.getCreateBy()), WcsIntf::getCreateBy, bo.getCreateBy());
        lqw.eq(bo.getCreateTime() != null, WcsIntf::getCreateTime, bo.getCreateTime());
        lqw.eq(StringUtils.isNotBlank(bo.getUpdateBy()), WcsIntf::getUpdateBy, bo.getUpdateBy());
        lqw.eq(bo.getUpdateTime() != null, WcsIntf::getUpdateTime, bo.getUpdateTime());
        lqw.eq(StringUtils.isNotBlank(bo.getDeleteBy()), WcsIntf::getDeleteBy, bo.getDeleteBy());
        lqw.eq(bo.getDeleteTime() != null, WcsIntf::getDeleteTime, bo.getDeleteTime());
        lqw.eq(bo.getRowVersionStamp() != null, WcsIntf::getRowVersionStamp, bo.getRowVersionStamp());
        lqw.eq(StringUtils.isNotBlank(bo.getRemark()), WcsIntf::getRemark, bo.getRemark());
        return lqw;
    }

    /**
     * 新增接口管理
     */
    @Override
    public Boolean insertByBo(WcsIntfBo bo) {
        WcsIntf add = BeanUtil.toBean(bo, WcsIntf.class);
        add.setFuid(IdUtil.fastUUID());
        validEntityBeforeSaveOrUpdate(add);
        return this.save(add);
    }

    /**
     * 修改接口管理
     */
    @Override
    public Boolean updateByBo(WcsIntfBo bo) {
        WcsIntf update = BeanUtil.toBean(bo, WcsIntf.class);
        validEntityBeforeSaveOrUpdate(update);
        return this.updateById(update);
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSaveOrUpdate(WcsIntf entity) {
        // 做一些数据校验,如唯一约束
        // 由于 getByInterfaceCode()，使用了interfaceCode作为缓存，所以修改数据库的时候也要删除掉缓存。
        CacheUtils.evict(CacheNames.ITF_CODE, entity.getInterfaceCode());
        CacheUtils.evict(CacheNames.ITF_CLASS, entity.getInterfaceClass());
    }

    /**
     * 批量删除接口管理
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids) {
        // 由于 getByInterfaceCode()，使用了interfaceCode作为缓存，所以删除数据库的时候也要删除掉缓存。
        for (Long id : ids) {
            WcsIntfVo intfVo = selectVoById(id);
            if (ObjectUtil.isNotNull(intfVo)) {
                CacheUtils.evict(CacheNames.ITF_CODE, intfVo.getInterfaceCode());
                CacheUtils.evict(CacheNames.ITF_CLASS, intfVo.getInterfaceClass());
            }
        }
        return this.removeBatchByIds(ids);
    }

    /**
     * 构建Http请求对象
     * SpringUtils.getBean(this.getClass()这种写法，是因为缓存是通过AOP实现的，所以同一个类里调用会造成切面失效
     *
     * @param interfaceRequest 接口请求对象
     * @return Http请求对象
     */
    @Override
    public HttpRequest getHttpRequest(InterfaceRequest interfaceRequest) {
        WcsIntf intf = SpringUtils.getBean(this.getClass()).getByInterfaceCode(interfaceRequest.getInterfaceCode().getCode());
        if (ObjectUtil.isNull(intf)) {
            throw new ServiceException(StrUtil.format("接口代码错误, code={}", interfaceRequest.getInterfaceCode().getCode()));
        }
        // 获取接口类型
        String interfaceType = intf.getInterfaceType();
        if (!StrUtil.equals(interfaceType, InterfaceTypeEnum.HTTP.name())) {
            throw new ServiceException(StrUtil.format("接口类型错误, type={}", interfaceType));
        }
        // 获取Http方法
        String method = intf.getMethod();
        if (!InterfaceMethodEnum.contains(method)) {
            throw new ServiceException(StrUtil.format("接口方法错误, method={}", method));
        }
        WcsExtApp extApp = extAppService.getByAppCode(intf.getAppCode());
        if (ObjectUtil.isNull(extApp)) {
            throw new ServiceException(StrUtil.format("系统定义对象错误, appCode={}", intf.getAppCode()));
        }
        // 获取IP
        String hostAddr = HttpUtil.isHttp(extApp.getHostAddr()) ? extApp.getHostAddr() : StrUtil.format("http://{}", extApp.getHostAddr());
        // 端口
        Long httpInterfacePort = ObjectUtil.isNull(extApp.getHttpInterfacePort()) ? 80 : extApp.getHttpInterfacePort();
        // 获取API,处理API上需要替换的信息
        String api = StrUtil.format(intf.getUrl(), JsonUtils.parseMap(interfaceRequest.getConvertObj()));
        if (!StrUtil.startWith(api, StrUtil.C_SLASH)) {
            api = StrUtil.format("{}{}", StrUtil.C_SLASH, api);
        }
        // 构建Http请求类
        HttpRequest result = HttpUtil.createRequest(InterfaceMethodEnum.getMethod(method), StrUtil.format("{}:{}{}", hostAddr, httpInterfacePort, api));
        // 处理头信息
        result.headerMap(JsonUtils.parseMapStr(interfaceRequest.getHeaderObj()), true);
        // 处理表单数据
        result.form(JsonUtils.parseMap(interfaceRequest.getParamObj()));
        // 处理post表单数据
        result.body(interfaceRequest.getBodyStr());
        // 设置超时时间
        if (ObjectUtil.isNotNull(intf.getTimeoutTime()) && intf.getTimeoutTime() > 0) {
            result.timeout((Integer.parseInt(intf.getTimeoutTime().toString())));
        }
        // 认证

        return result;
    }

    /**
     * 获取重试次数
     * SpringUtils.getBean(this.getClass()这种写法，是因为缓存是通过AOP实现的，所以同一个类里调用会造成切面失效
     *
     * @param interfaceRequest 接口请求对象
     * @return 重试次数
     */
    @Override
    public Long getRetryCount(InterfaceRequest interfaceRequest) {
        WcsIntf intf = SpringUtils.getBean(this.getClass()).getByInterfaceCode(interfaceRequest.getInterfaceCode().getCode());
        if (ObjectUtil.isNotNull(intf) && intf.isRetry() && ObjectUtil.isNotNull(intf.getMaxRetry()) && intf.getMaxRetry() > 0L) {
            return intf.getMaxRetry();
        }
        return 1L;
    }

    /**
     * 通过类型查询接口列表
     */
    @Cacheable(cacheNames = CacheNames.ITF_CLASS, key = "#classEnum.code")
    @Override
    public List<WcsIntf> listByClass(InterfaceClassEnum classEnum) {
        LambdaQueryWrapper<WcsIntf> lqw = Wrappers.lambdaQuery();
        lqw.eq(WcsIntf::getInterfaceClass, classEnum.getCode());
        return this.baseMapper.selectList(lqw);
    }

    /**
     * 获取数据库中已创建好的接口编码
     */
    @Override
    public List<String> listInterfaceCodeInDB() {
        List<WcsIntf> intfList = this.list();
        return CollUtil.isEmpty(intfList) ? new ArrayList<>() : intfList.stream().map(WcsIntf::getInterfaceCode).collect(Collectors.toList());
    }
}
